/*! @azure/msal-browser v2.38.3 2023-10-27 */
'use strict';
import { __extends, __awaiter, __generator, __assign } from '../_virtual/_tslib.js';
import { OIDC_DEFAULT_SCOPES, StringUtils, UrlString, Constants, AuthError, ThrottlingUtils, ProtocolUtils, PerformanceEvents } from '@azure/msal-common';
import { StandardInteractionClient } from './StandardInteractionClient.js';
import { EventType } from '../event/EventType.js';
import { BrowserConstants, InteractionType, ApiId } from '../utils/BrowserConstants.js';
import { BrowserUtils } from '../utils/BrowserUtils.js';
import { NativeInteractionClient } from './NativeInteractionClient.js';
import { NativeMessageHandler } from '../broker/nativeBroker/NativeMessageHandler.js';
import { BrowserAuthError } from '../error/BrowserAuthError.js';
import { InteractionHandler } from '../interaction_handler/InteractionHandler.js';

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
var PopupClient = /** @class */ (function (_super) {
    __extends(PopupClient, _super);
    function PopupClient(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, nativeStorageImpl, nativeMessageHandler, correlationId) {
        var _this = _super.call(this, config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, nativeMessageHandler, correlationId) || this;
        // Properly sets this reference for the unload event.
        _this.unloadWindow = _this.unloadWindow.bind(_this);
        _this.nativeStorage = nativeStorageImpl;
        return _this;
    }
    /**
     * Acquires tokens by opening a popup window to the /authorize endpoint of the authority
     * @param request
     */
    PopupClient.prototype.acquireToken = function (request) {
        try {
            var popupName = this.generatePopupName(request.scopes || OIDC_DEFAULT_SCOPES, request.authority || this.config.auth.authority);
            var popupWindowAttributes = request.popupWindowAttributes || {};
            // asyncPopups flag is true. Acquires token without first opening popup. Popup will be opened later asynchronously.
            if (this.config.system.asyncPopups) {
                this.logger.verbose("asyncPopups set to true, acquiring token");
                // Passes on popup position and dimensions if in request
                return this.acquireTokenPopupAsync(request, popupName, popupWindowAttributes);
            }
            else {
                // asyncPopups flag is set to false. Opens popup before acquiring token.
                this.logger.verbose("asyncPopup set to false, opening popup before acquiring token");
                var popup = this.openSizedPopup("about:blank", popupName, popupWindowAttributes);
                return this.acquireTokenPopupAsync(request, popupName, popupWindowAttributes, popup);
            }
        }
        catch (e) {
            return Promise.reject(e);
        }
    };
    /**
     * Clears local cache for the current user then opens a popup window prompting the user to sign-out of the server
     * @param logoutRequest
     */
    PopupClient.prototype.logout = function (logoutRequest) {
        try {
            this.logger.verbose("logoutPopup called");
            var validLogoutRequest = this.initializeLogoutRequest(logoutRequest);
            var popupName = this.generateLogoutPopupName(validLogoutRequest);
            var authority = logoutRequest && logoutRequest.authority;
            var mainWindowRedirectUri = logoutRequest && logoutRequest.mainWindowRedirectUri;
            var popupWindowAttributes = (logoutRequest === null || logoutRequest === void 0 ? void 0 : logoutRequest.popupWindowAttributes) || {};
            // asyncPopups flag is true. Acquires token without first opening popup. Popup will be opened later asynchronously.
            if (this.config.system.asyncPopups) {
                this.logger.verbose("asyncPopups set to true");
                // Passes on popup position and dimensions if in request
                return this.logoutPopupAsync(validLogoutRequest, popupName, popupWindowAttributes, authority, undefined, mainWindowRedirectUri);
            }
            else {
                // asyncPopups flag is set to false. Opens popup before logging out.
                this.logger.verbose("asyncPopup set to false, opening popup");
                var popup = this.openSizedPopup("about:blank", popupName, popupWindowAttributes);
                return this.logoutPopupAsync(validLogoutRequest, popupName, popupWindowAttributes, authority, popup, mainWindowRedirectUri);
            }
        }
        catch (e) {
            // Since this function is synchronous we need to reject
            return Promise.reject(e);
        }
    };
    /**
     * Helper which obtains an access_token for your API via opening a popup window in the user's browser
     * @param validRequest
     * @param popupName
     * @param popup
     * @param popupWindowAttributes
     *
     * @returns A promise that is fulfilled when this function has completed, or rejected if an error was raised.
     */
    PopupClient.prototype.acquireTokenPopupAsync = function (request, popupName, popupWindowAttributes, popup) {
        return __awaiter(this, void 0, void 0, function () {
            var serverTelemetryManager, validRequest, authCodeRequest, authClient, isNativeBroker, fetchNativeAccountIdMeasurement, navigateUrl, interactionHandler, popupParameters, popupWindow, hash, serverParams, state_1, nativeInteractionClient, userRequestState, result, e_1;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.logger.verbose("acquireTokenPopupAsync called");
                        serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenPopup);
                        this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientInitializeAuthorizationRequest, request.correlationId);
                        return [4 /*yield*/, this.initializeAuthorizationRequest(request, InteractionType.Popup)];
                    case 1:
                        validRequest = _a.sent();
                        this.browserStorage.updateCacheEntries(validRequest.state, validRequest.nonce, validRequest.authority, validRequest.loginHint || Constants.EMPTY_STRING, validRequest.account || null);
                        _a.label = 2;
                    case 2:
                        _a.trys.push([2, 8, , 9]);
                        // Create auth code request and generate PKCE params
                        this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientInitializeAuthorizationCodeRequest, request.correlationId);
                        return [4 /*yield*/, this.initializeAuthorizationCodeRequest(validRequest)];
                    case 3:
                        authCodeRequest = _a.sent();
                        // Initialize the client
                        this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, request.correlationId);
                        return [4 /*yield*/, this.createAuthCodeClient(serverTelemetryManager, validRequest.authority, validRequest.azureCloudOptions)];
                    case 4:
                        authClient = _a.sent();
                        this.logger.verbose("Auth code client created");
                        isNativeBroker = NativeMessageHandler.isNativeAvailable(this.config, this.logger, this.nativeMessageHandler, request.authenticationScheme);
                        fetchNativeAccountIdMeasurement = void 0;
                        if (isNativeBroker) {
                            fetchNativeAccountIdMeasurement = this.performanceClient.startMeasurement(PerformanceEvents.FetchAccountIdWithNativeBroker, request.correlationId);
                        }
                        return [4 /*yield*/, authClient.getAuthCodeUrl(__assign(__assign({}, validRequest), { nativeBroker: isNativeBroker }))];
                    case 5:
                        navigateUrl = _a.sent();
                        interactionHandler = new InteractionHandler(authClient, this.browserStorage, authCodeRequest, this.logger, this.performanceClient);
                        popupParameters = {
                            popup: popup,
                            popupName: popupName,
                            popupWindowAttributes: popupWindowAttributes
                        };
                        popupWindow = this.initiateAuthRequest(navigateUrl, popupParameters);
                        this.eventHandler.emitEvent(EventType.POPUP_OPENED, InteractionType.Popup, { popupWindow: popupWindow }, null);
                        return [4 /*yield*/, this.monitorPopupForHash(popupWindow)];
                    case 6:
                        hash = _a.sent();
                        serverParams = UrlString.getDeserializedHash(hash);
                        state_1 = this.validateAndExtractStateFromHash(serverParams, InteractionType.Popup, validRequest.correlationId);
                        // Remove throttle if it exists
                        ThrottlingUtils.removeThrottle(this.browserStorage, this.config.auth.clientId, authCodeRequest);
                        if (serverParams.accountId) {
                            this.logger.verbose("Account id found in hash, calling WAM for token");
                            // end measurement for server call with native brokering enabled
                            if (fetchNativeAccountIdMeasurement) {
                                fetchNativeAccountIdMeasurement.endMeasurement({
                                    success: true,
                                    isNativeBroker: true
                                });
                            }
                            if (!this.nativeMessageHandler) {
                                throw BrowserAuthError.createNativeConnectionNotEstablishedError();
                            }
                            nativeInteractionClient = new NativeInteractionClient(this.config, this.browserStorage, this.browserCrypto, this.logger, this.eventHandler, this.navigationClient, ApiId.acquireTokenPopup, this.performanceClient, this.nativeMessageHandler, serverParams.accountId, this.nativeStorage, validRequest.correlationId);
                            userRequestState = ProtocolUtils.parseRequestState(this.browserCrypto, state_1).userRequestState;
                            return [2 /*return*/, nativeInteractionClient.acquireToken(__assign(__assign({}, validRequest), { state: userRequestState, prompt: undefined // Server should handle the prompt, ideally native broker can do this part silently
                                 })).finally(function () {
                                    _this.browserStorage.cleanRequestByState(state_1);
                                })];
                        }
                        return [4 /*yield*/, interactionHandler.handleCodeResponseFromHash(hash, state_1, authClient.authority, this.networkClient)];
                    case 7:
                        result = _a.sent();
                        return [2 /*return*/, result];
                    case 8:
                        e_1 = _a.sent();
                        if (popup) {
                            // Close the synchronous popup if an error is thrown before the window unload event is registered
                            popup.close();
                        }
                        if (e_1 instanceof AuthError) {
                            e_1.setCorrelationId(this.correlationId);
                        }
                        serverTelemetryManager.cacheFailedRequest(e_1);
                        this.browserStorage.cleanRequestByState(validRequest.state);
                        throw e_1;
                    case 9: return [2 /*return*/];
                }
            });
        });
    };
    /**
     *
     * @param validRequest
     * @param popupName
     * @param requestAuthority
     * @param popup
     * @param mainWindowRedirectUri
     * @param popupWindowAttributes
     */
    PopupClient.prototype.logoutPopupAsync = function (validRequest, popupName, popupWindowAttributes, requestAuthority, popup, mainWindowRedirectUri) {
        return __awaiter(this, void 0, void 0, function () {
            var serverTelemetryManager, authClient, logoutUri, popupWindow, navigationOptions, absoluteUrl, e_2;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.logger.verbose("logoutPopupAsync called");
                        this.eventHandler.emitEvent(EventType.LOGOUT_START, InteractionType.Popup, validRequest);
                        serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.logoutPopup);
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 5, , 6]);
                        // Clear cache on logout
                        return [4 /*yield*/, this.clearCacheOnLogout(validRequest.account)];
                    case 2:
                        // Clear cache on logout
                        _a.sent();
                        // Initialize the client
                        this.performanceClient.setPreQueueTime(PerformanceEvents.StandardInteractionClientCreateAuthCodeClient, validRequest.correlationId);
                        return [4 /*yield*/, this.createAuthCodeClient(serverTelemetryManager, requestAuthority)];
                    case 3:
                        authClient = _a.sent();
                        this.logger.verbose("Auth code client created");
                        logoutUri = authClient.getLogoutUri(validRequest);
                        this.eventHandler.emitEvent(EventType.LOGOUT_SUCCESS, InteractionType.Popup, validRequest);
                        popupWindow = this.openPopup(logoutUri, { popupName: popupName, popupWindowAttributes: popupWindowAttributes, popup: popup });
                        this.eventHandler.emitEvent(EventType.POPUP_OPENED, InteractionType.Popup, { popupWindow: popupWindow }, null);
                        return [4 /*yield*/, this.waitForLogoutPopup(popupWindow)];
                    case 4:
                        _a.sent();
                        if (mainWindowRedirectUri) {
                            navigationOptions = {
                                apiId: ApiId.logoutPopup,
                                timeout: this.config.system.redirectNavigationTimeout,
                                noHistory: false
                            };
                            absoluteUrl = UrlString.getAbsoluteUrl(mainWindowRedirectUri, BrowserUtils.getCurrentUri());
                            this.logger.verbose("Redirecting main window to url specified in the request");
                            this.logger.verbosePii("Redirecting main window to: " + absoluteUrl);
                            this.navigationClient.navigateInternal(absoluteUrl, navigationOptions);
                        }
                        else {
                            this.logger.verbose("No main window navigation requested");
                        }
                        return [3 /*break*/, 6];
                    case 5:
                        e_2 = _a.sent();
                        if (popup) {
                            // Close the synchronous popup if an error is thrown before the window unload event is registered
                            popup.close();
                        }
                        if (e_2 instanceof AuthError) {
                            e_2.setCorrelationId(this.correlationId);
                        }
                        this.browserStorage.setInteractionInProgress(false);
                        this.eventHandler.emitEvent(EventType.LOGOUT_FAILURE, InteractionType.Popup, null, e_2);
                        this.eventHandler.emitEvent(EventType.LOGOUT_END, InteractionType.Popup);
                        serverTelemetryManager.cacheFailedRequest(e_2);
                        throw e_2;
                    case 6:
                        this.eventHandler.emitEvent(EventType.LOGOUT_END, InteractionType.Popup);
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Opens a popup window with given request Url.
     * @param requestUrl
     */
    PopupClient.prototype.initiateAuthRequest = function (requestUrl, params) {
        // Check that request url is not empty.
        if (!StringUtils.isEmpty(requestUrl)) {
            this.logger.infoPii("Navigate to: " + requestUrl);
            // Open the popup window to requestUrl.
            return this.openPopup(requestUrl, params);
        }
        else {
            // Throw error if request URL is empty.
            this.logger.error("Navigate url is empty");
            throw BrowserAuthError.createEmptyNavigationUriError();
        }
    };
    /**
     * Monitors a window until it loads a url with the same origin.
     * @param popupWindow - window that is being monitored
     * @param timeout - timeout for processing hash once popup is redirected back to application
     */
    PopupClient.prototype.monitorPopupForHash = function (popupWindow) {
        var _this = this;
        return new Promise(function (resolve, reject) {
            /*
             * Polling for popups needs to be tick-based,
             * since a non-trivial amount of time can be spent on interaction (which should not count against the timeout).
             */
            var maxTicks = _this.config.system.windowHashTimeout / _this.config.system.pollIntervalMilliseconds;
            var ticks = 0;
            _this.logger.verbose("PopupHandler.monitorPopupForHash - polling started");
            var intervalId = setInterval(function () {
                // Window is closed
                if (popupWindow.closed) {
                    _this.logger.error("PopupHandler.monitorPopupForHash - window closed");
                    _this.cleanPopup();
                    clearInterval(intervalId);
                    reject(BrowserAuthError.createUserCancelledError());
                    return;
                }
                var href = Constants.EMPTY_STRING;
                var hash = Constants.EMPTY_STRING;
                try {
                    /*
                     * Will throw if cross origin,
                     * which should be caught and ignored
                     * since we need the interval to keep running while on STS UI.
                     */
                    href = popupWindow.location.href;
                    hash = popupWindow.location.hash;
                }
                catch (e) { }
                // Don't process blank pages or cross domain
                if (StringUtils.isEmpty(href) || href === "about:blank") {
                    return;
                }
                _this.logger.verbose("PopupHandler.monitorPopupForHash - popup window is on same origin as caller");
                /*
                 * Only run clock when we are on same domain for popups
                 * as popup operations can take a long time.
                 */
                ticks++;
                if (hash) {
                    _this.logger.verbose("PopupHandler.monitorPopupForHash - found hash in url");
                    clearInterval(intervalId);
                    _this.cleanPopup(popupWindow);
                    if (UrlString.hashContainsKnownProperties(hash)) {
                        _this.logger.verbose("PopupHandler.monitorPopupForHash - hash contains known properties, returning.");
                        resolve(hash);
                    }
                    else {
                        _this.logger.error("PopupHandler.monitorPopupForHash - found hash in url but it does not contain known properties. Check that your router is not changing the hash prematurely.");
                        _this.logger.errorPii("PopupHandler.monitorPopupForHash - hash found: " + hash);
                        reject(BrowserAuthError.createHashDoesNotContainKnownPropertiesError());
                    }
                }
                else if (ticks > maxTicks) {
                    _this.logger.error("PopupHandler.monitorPopupForHash - unable to find hash in url, timing out");
                    clearInterval(intervalId);
                    reject(BrowserAuthError.createMonitorPopupTimeoutError());
                }
            }, _this.config.system.pollIntervalMilliseconds);
        });
    };
    /**
     * Waits for user interaction in logout popup window
     * @param popupWindow
     * @returns
     */
    PopupClient.prototype.waitForLogoutPopup = function (popupWindow) {
        var _this = this;
        return new Promise(function (resolve) {
            _this.logger.verbose("PopupHandler.waitForLogoutPopup - polling started");
            var intervalId = setInterval(function () {
                // Window is closed
                if (popupWindow.closed) {
                    _this.logger.error("PopupHandler.waitForLogoutPopup - window closed");
                    _this.cleanPopup();
                    clearInterval(intervalId);
                    resolve();
                }
                var href = Constants.EMPTY_STRING;
                try {
                    /*
                     * Will throw if cross origin,
                     * which should be caught and ignored
                     * since we need the interval to keep running while on STS UI.
                     */
                    href = popupWindow.location.href;
                }
                catch (e) { }
                // Don't process blank pages or cross domain
                if (StringUtils.isEmpty(href) || href === "about:blank") {
                    return;
                }
                _this.logger.verbose("PopupHandler.waitForLogoutPopup - popup window is on same origin as caller, closing.");
                clearInterval(intervalId);
                _this.cleanPopup(popupWindow);
                resolve();
            }, _this.config.system.pollIntervalMilliseconds);
        });
    };
    /**
     * @hidden
     *
     * Configures popup window for login.
     *
     * @param urlNavigate
     * @param title
     * @param popUpWidth
     * @param popUpHeight
     * @param popupWindowAttributes
     * @ignore
     * @hidden
     */
    PopupClient.prototype.openPopup = function (urlNavigate, popupParams) {
        try {
            var popupWindow = void 0;
            // Popup window passed in, setting url to navigate to
            if (popupParams.popup) {
                popupWindow = popupParams.popup;
                this.logger.verbosePii("Navigating popup window to: " + urlNavigate);
                popupWindow.location.assign(urlNavigate);
            }
            else if (typeof popupParams.popup === "undefined") {
                // Popup will be undefined if it was not passed in
                this.logger.verbosePii("Opening popup window to: " + urlNavigate);
                popupWindow = this.openSizedPopup(urlNavigate, popupParams.popupName, popupParams.popupWindowAttributes);
            }
            // Popup will be null if popups are blocked
            if (!popupWindow) {
                throw BrowserAuthError.createEmptyWindowCreatedError();
            }
            if (popupWindow.focus) {
                popupWindow.focus();
            }
            this.currentWindow = popupWindow;
            window.addEventListener("beforeunload", this.unloadWindow);
            return popupWindow;
        }
        catch (e) {
            this.logger.error("error opening popup " + e.message);
            this.browserStorage.setInteractionInProgress(false);
            throw BrowserAuthError.createPopupWindowError(e.toString());
        }
    };
    /**
     * Helper function to set popup window dimensions and position
     * @param urlNavigate
     * @param popupName
     * @param popupWindowAttributes
     * @returns
     */
    PopupClient.prototype.openSizedPopup = function (urlNavigate, popupName, popupWindowAttributes) {
        var _a, _b, _c, _d;
        /**
         * adding winLeft and winTop to account for dual monitor
         * using screenLeft and screenTop for IE8 and earlier
         */
        var winLeft = window.screenLeft ? window.screenLeft : window.screenX;
        var winTop = window.screenTop ? window.screenTop : window.screenY;
        /**
         * window.innerWidth displays browser window"s height and width excluding toolbars
         * using document.documentElement.clientWidth for IE8 and earlier
         */
        var winWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
        var winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
        var width = (_a = popupWindowAttributes.popupSize) === null || _a === void 0 ? void 0 : _a.width;
        var height = (_b = popupWindowAttributes.popupSize) === null || _b === void 0 ? void 0 : _b.height;
        var top = (_c = popupWindowAttributes.popupPosition) === null || _c === void 0 ? void 0 : _c.top;
        var left = (_d = popupWindowAttributes.popupPosition) === null || _d === void 0 ? void 0 : _d.left;
        if (!width || width < 0 || width > winWidth) {
            this.logger.verbose("Default popup window width used. Window width not configured or invalid.");
            width = BrowserConstants.POPUP_WIDTH;
        }
        if (!height || height < 0 || height > winHeight) {
            this.logger.verbose("Default popup window height used. Window height not configured or invalid.");
            height = BrowserConstants.POPUP_HEIGHT;
        }
        if (!top || top < 0 || top > winHeight) {
            this.logger.verbose("Default popup window top position used. Window top not configured or invalid.");
            top = Math.max(0, ((winHeight / 2) - (BrowserConstants.POPUP_HEIGHT / 2)) + winTop);
        }
        if (!left || left < 0 || left > winWidth) {
            this.logger.verbose("Default popup window left position used. Window left not configured or invalid.");
            left = Math.max(0, ((winWidth / 2) - (BrowserConstants.POPUP_WIDTH / 2)) + winLeft);
        }
        return window.open(urlNavigate, popupName, "width=" + width + ", height=" + height + ", top=" + top + ", left=" + left + ", scrollbars=yes");
    };
    /**
     * Event callback to unload main window.
     */
    PopupClient.prototype.unloadWindow = function (e) {
        this.browserStorage.cleanRequestByInteractionType(InteractionType.Popup);
        if (this.currentWindow) {
            this.currentWindow.close();
        }
        // Guarantees browser unload will happen, so no other errors will be thrown.
        e.preventDefault();
    };
    /**
     * Closes popup, removes any state vars created during popup calls.
     * @param popupWindow
     */
    PopupClient.prototype.cleanPopup = function (popupWindow) {
        if (popupWindow) {
            // Close window.
            popupWindow.close();
        }
        // Remove window unload function
        window.removeEventListener("beforeunload", this.unloadWindow);
        // Interaction is completed - remove interaction status.
        this.browserStorage.setInteractionInProgress(false);
    };
    /**
     * Generates the name for the popup based on the client id and request
     * @param clientId
     * @param request
     */
    PopupClient.prototype.generatePopupName = function (scopes, authority) {
        return BrowserConstants.POPUP_NAME_PREFIX + "." + this.config.auth.clientId + "." + scopes.join("-") + "." + authority + "." + this.correlationId;
    };
    /**
     * Generates the name for the popup based on the client id and request for logouts
     * @param clientId
     * @param request
     */
    PopupClient.prototype.generateLogoutPopupName = function (request) {
        var homeAccountId = request.account && request.account.homeAccountId;
        return BrowserConstants.POPUP_NAME_PREFIX + "." + this.config.auth.clientId + "." + homeAccountId + "." + this.correlationId;
    };
    return PopupClient;
}(StandardInteractionClient));

export { PopupClient };
//# sourceMappingURL=PopupClient.js.map
